package orm

import (
	"context"
	"errors"
	"gitlab.xchch.top/zhangsai/training-homework/orm2/internal/errs"
	"reflect"
)

type Updater[T any] struct {
	builder
	db      *DB
	assigns []Assignable
	val     *T
	where   []Predicate
}

func NewUpdater[T any](db *DB) *Updater[T] {
	return &Updater[T]{
		db: db,
		builder: builder{
			dialect: db.dialect,
			quoter:  db.dialect.quoter(),
		},
	}
}

func (u *Updater[T]) Update(t *T) *Updater[T] {
	u.val = t
	return u
}

func (u *Updater[T]) Set(assigns ...Assignable) *Updater[T] {
	u.assigns = assigns
	return u
}

func (u *Updater[T]) Build() (*Query, error) {
	if len(u.assigns) == 0 {
		return nil, errs.ErrNoUpdatedColumns
	}
	if u.val == nil {
		return nil, errors.New("找不到model")
	}

	model, err := u.db.r.Get(u.val)
	if err != nil {
		return nil, err
	}
	u.model = model
	u.sb.WriteString("UPDATE ")
	u.quote(u.model.TableName)
	u.sb.WriteString(" SET ")

	// 指针转结构体
	refVal := reflect.ValueOf(u.val)
	typ := refVal.Type()
	if typ.Kind() == reflect.Ptr {
		refVal = refVal.Elem()
	}

	// 拼接set部分
	for idx, assign := range u.assigns {
		if idx > 0 {
			u.sb.WriteByte(',')
		}

		switch assType := assign.(type) {
		case Column:
			field, ok := u.model.FieldMap[assType.name]
			if !ok {
				return nil, errs.NewErrUnknownField(assType.name)
			}
			u.quote(field.ColName)
			u.sb.WriteString("=?")

			arg := refVal.FieldByName(assType.name).Interface()
			u.args = append(u.args, arg)
		case Assignment:
			err := u.builder.buildColumn(assType.column)
			if err != nil {
				return nil, err
			}
			u.sb.WriteString("=?")

			u.args = append(u.args, assType.val.(value).val)
		}
	}

	// 拼接where部分
	if len(u.where) > 0 {
		u.sb.WriteString(" WHERE ")
		err := u.buildPredicates(u.where)
		if err != nil {
			return nil, err
		}
	}

	u.sb.WriteByte(';')
	return &Query{SQL: u.sb.String(), Args: u.args}, nil
}

func (u *Updater[T]) Where(ps ...Predicate) *Updater[T] {
	u.where = ps
	return u
}

func (u *Updater[T]) Exec(ctx context.Context) Result {
	panic("implement me")
}

// AssignNotZeroColumns 更新非零值
func AssignNotZeroColumns(entity interface{}) []Assignable {
	panic("implement me")
}
